/****************************************************
	文件：ObjectPool.cs
	作者：HuskyT
	邮箱：1005240602@qq.com
	日期：2021/7/4 19:58:0
	功能：对象池（对象需要继承 IPoolable 接口）
*****************************************************/

using UnityEngine;

namespace HTFramework.Core
{
    /// <summary>
    /// 对象池创建（委托）
    /// </summary>
    public delegate T ObjectPoolCreator<T>(ObjectPool<T> pool) where T : class, IPoolable;

    /// <summary>
    /// 对象池处理（委托）
    /// </summary>
    public delegate void ObjectPoolHandler<in T>(T obj) where T : class, IPoolable;

    public class ObjectPool<T> : IObjectPool<T>
        where T : class, IPoolable
    {
        /*
         * 原理：
         * 对象分配和回收的过程中只移动指针，减少了出入队/出入栈的消耗，性能相对较高
         * 对对象的控制能力高于使用单个队列/栈的对象池
         */

        /// <summary>
        /// 对象池容器
        /// </summary>
        T[] mPool;

        /// <summary>
        /// 当前待分配的对象的索引（在分配新对象中++；回收对象中--）
        /// </summary>
        int mCurPrepareIndex;

        /// <summary>
        /// 容器容量（初始化赋值后，只在扩容中自动更新）
        /// </summary>
        int mCapcity;

        /// <summary>
        /// 对象池内对象个数
        /// </summary>
        int mCnt;

        ObjectPoolCreator<T> mCreator;
        ObjectPoolHandler<T> mInitializer;
        ObjectPoolHandler<T> mDeinitializer;

        /// <summary>
        /// 创建对象池
        /// </summary>
        /// <param name="initialCapcity">初始对象池容量</param>
        /// <param name="creator">对象创建接口</param>
        /// <param name="initializer">对象分配前初始化接口</param>
        /// <param name="deinitializer">对象回收时还原接口</param>
        public ObjectPool(int initialCapcity, ObjectPoolCreator<T> creator, ObjectPoolHandler<T> initializer,
            ObjectPoolHandler<T> deinitializer)
        {
            mCnt = mCapcity = initialCapcity;
            mPool = new T[mCapcity];
            mCurPrepareIndex = 0;

            mCreator = creator;
            mInitializer = initializer;
            mDeinitializer = deinitializer;

            T obj;
            for (int i = 0; i < initialCapcity; i++)
            {
                obj = mCreator(this);
                obj.ObjectPoolIndex = i;
                mPool[i] = obj;
            }
        }

        public void Dispose()
        {
            DespawnAll();

            for (int i = 0; i < mCnt; i++)
            {
                mPool[i].Dispose();
            }

            mPool = null;
            mCreator = null;
            mInitializer = null;
            mDeinitializer = null;
        }

        /// <summary>
        /// 分配对象
        /// </summary>
        /// <param name="ignoreCapcity">是否忽略容量，容量不足时扩容并创建新对象</param>
        /// <returns></returns>
        public T Spawn(bool ignoreCapcity = true)
        {
            T ret;
            if (mCurPrepareIndex >= mCapcity) //容器容量不够
            {
                if (ignoreCapcity)
                {
                    mPool = ExpandPool(); //扩容
                    ret = mCreator(this); //创建新对象
                    ret.ObjectPoolIndex = mCnt;
                    mPool[mCnt] = ret;


                    mCnt++;
                    mCurPrepareIndex++;

                    mInitializer(ret);
                }
                else
                {
                    ret = null;
                }
            }
            else //容器容量充足
            {
                if (mCurPrepareIndex >= mCnt) //现有对象已分配完
                {
                    if (ignoreCapcity)
                    {
                        ret = mCreator(this); //创建新对象
                        ret.ObjectPoolIndex = mCnt;
                        mPool[mCnt] = ret;

                        mCnt++;
                        mCurPrepareIndex++;

                        mInitializer(ret);
                    }
                    else
                    {
                        ret = null;
                    }
                }
                else //现有对象有剩余
                {
                    ret = mPool[mCurPrepareIndex];

                    mCurPrepareIndex++;

                    mInitializer(ret);
                }
            }

            return ret;
        }

        /// <summary>
        /// 回收指定对象（destory 效率较低，慎用）
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="destory"></param>
        public void Despawn(T obj, bool destory = false) //destory效率较低，慎用
        {
            if (mCurPrepareIndex == 0)
            {
#if UNITY_EDITOR
                Debug.LogError("ObjectPool despawn error! 对象池尚未分配对象");
#endif
                return;
            }

            if (obj == null)
            {
#if UNITY_EDITOR
                Debug.LogError("ObjectPool despawn error! 检查是否重复回收对象");
#endif
                return;
            }

            int despawnIndex = obj.ObjectPoolIndex;
            if (despawnIndex >= mCurPrepareIndex) return; //对象已被回收，避免重复回收

            if (destory)
            {
                Swap(despawnIndex, mCurPrepareIndex - 1); //交换
                for (int i = mCurPrepareIndex; i < mCnt; i++)
                {
                    Swap(i - 1, i); //前移一个位置
                }

                mDeinitializer(obj);
                obj.Dispose();

                mCnt--;
                mCurPrepareIndex--;
            }
            else
            {
                Swap(despawnIndex, mCurPrepareIndex - 1); //交换

                mDeinitializer(obj);

                mCurPrepareIndex--;
            }
        }

        /// <summary>
        /// 回收所有分配出去的对象
        /// </summary>
        /// <param name="destory"></param>
        public void DespawnAll(bool destory = false)
        {
            if (destory)
            {
                for (int i = 0; i < mCurPrepareIndex; i++)
                {
                    mDeinitializer(mPool[i]);
                    mPool[i].Dispose();
                }

                int tempIndex = 0;
                for (int i = mCurPrepareIndex; i < mCnt; i++)
                {
                    Swap(tempIndex, i);
                    tempIndex++;
                }

                mCnt -= mCurPrepareIndex;
                mCurPrepareIndex = 0;
            }
            else
            {
                for (int i = 0; i < mCurPrepareIndex; i++)
                {
                    mDeinitializer(mPool[i]);
                }

                mCurPrepareIndex = 0;
            }
        }

        /// <summary>
        /// 容器扩容
        /// </summary>
        T[] ExpandPool()
        {
            mCapcity *= 2;
            T[] newPool = new T[mCapcity];
            for (int i = 0; i < mPool.Length; i++)
            {
                newPool[i] = mPool[i];
            }

            return newPool;
        }

        /// <summary>
        /// 交换对象在容器中的位置
        /// </summary>
        void Swap(int a, int b)
        {
            T temp = mPool[a];
            temp.ObjectPoolIndex = b;

            mPool[a] = mPool[b];
            mPool[b] = temp;

            mPool[a].ObjectPoolIndex = a;
        }
    }
}